package com.aforce3.trailertracer;

import java.awt.image.BufferedImage;

import javax.vecmath.Color3f;
import javax.vecmath.Vector3d;

import com.aforce3.trailertracer.utils.ColorModifier;

/** Renderer should contact the FrameServer to get it's next assignment, process the data, and submit changes **/
public class Renderer extends Thread
{
	final FrameServer server;
	int[] primes = { 1, 13, 19, 29, 37, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97 };
	volatile boolean terminate = false;

	public Renderer(FrameServer server)
	{
		this.server = server;
		
		// We don't want to interfere with more visible operations like frame swapping
		setPriority(Thread.MIN_PRIORITY);
	}

	public void render(Frame job, int x, int y)
	{
		BufferedImage image = job.getImage();
		
		Vector3d vertical = new Vector3d(job.getCamera().up);
		Vector3d horizontal = new Vector3d();
		horizontal.cross(job.getCamera().up, job.getCamera().getDirection());
		
		vertical.normalize();
		horizontal.normalize();

		double verticalStep = (job.getCamera().fov*image.getHeight()/image.getWidth())/image.getHeight();
		verticalStep = (job.getCamera().fov)/image.getHeight();
		double horizontalStep = job.getCamera().fov/image.getWidth();

		double antialiasing_x = 0;
		double antialiasing_y = 0;
		
		if(SettingsManager.ANTIALIASING)
		{
			double theta = Math.random()*2*Math.PI;
			double r = Math.random();
			antialiasing_x = Math.sin(theta) * r;
			antialiasing_y = Math.cos(theta) * r;
			
			antialiasing_x *= 3;
			antialiasing_y *= 3;
		}

		double horizontalAngle = horizontalStep * (-(x+antialiasing_x)+image.getWidth()/2);
		double verticalAngle = verticalStep * (-(y+antialiasing_y)+image.getHeight()/2);
		
		vertical.scale(Math.tan(verticalAngle));
		horizontal.scale(Math.tan(horizontalAngle));

		Vector3d direction = new Vector3d(job.getCamera().getDirection());
		direction.add(vertical);
		direction.add(horizontal);
		
		direction.normalize();

		Ray ray = new Ray(job.getCamera().getPosition(), direction, 1);


		Color3f discovered = job.getScene().getColor(ray);
		if(discovered == null) discovered = ColorModifier.BLACK;
		Color3f initial = ColorModifier.toColor3f(image.getRGB(x, y));

		Color3f newColor = ColorModifier.merge(job.getPassesCompleted(), initial, discovered);
		image.setRGB(x, y, ColorModifier.toInt(newColor));
	}

	public void run()
	{
		while(!terminate)
		{
			method_standard(server.getJob());
		}
	}

	public void method_standard(Frame job)
	{
		BufferedImage image = job.getImage();

		for(int i = 0; i < image.getHeight() * image.getWidth() && !terminate; i++)
		{
			// Get a pixel
			int x = i % image.getWidth();
			int y = i / image.getWidth();
			render(job, x, y);
		}
		job.completed(1);
	}

	public void method_random(Frame job)
	{
		BufferedImage image = job.getImage();

		for(int j = 0; j < 1000 && !terminate; j++)
		{
			int i = (int) (Math.random() * image.getHeight() * image.getWidth());

			// Get a pixel
			int x = i % image.getWidth();
			int y = i / image.getWidth();
			render(job, x, y);
		}
		job.completed(100.0/(image.getHeight()*image.getWidth()));
	}
	
	public void terminate()
	{
		terminate = true;
	}
}
